package com.naza.rpc.circuitBreaker;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

/**
 * A CircuitBreaker finite state machine.
 *
 * @author yl
 */
final class CircuitBreakerImpl implements CircuitBreaker {
    private Logger logger = LoggerFactory.getLogger(CircuitBreakerImpl.class);

    private final String name;

    // 熔断器状态的原子引用
    private final AtomicReference<CircuitBreakerState> stateReference;

    // 熔断器的配置
    private final CircuitBreakerConfig circuitBreakerConfig;

    //当前调用总次数(调用前加1，调用后减1)
    private final AtomicLong currentExecutions = new AtomicLong();

    /**
     * Creates a circuitBreaker.
     *
     * @param name
     *            the name of the CircuitBreaker
     * @param circuitBreakerConfig
     *            The CircuitBreaker configuration.
     */
    CircuitBreakerImpl(String name, CircuitBreakerConfig circuitBreakerConfig) {
        this.name = name;
        this.circuitBreakerConfig = circuitBreakerConfig;
        this.stateReference = new AtomicReference<CircuitBreakerState>(new ClosedState(this));
    }

    /**
     * Requests permission to call this backend.
     *
     * @return true, if the call is allowed.
     */
    @Override
    public boolean isCallPermitted() {
        return stateReference.get().isCallPermitted();
    }

    /**
     * Records a failed call.
     */
    @Override
    public void recordFailure(Throwable throwable) {
        if (circuitBreakerConfig.isFailure(throwable)) {
            stateReference.get().recordFailure();
        }
    }

    @Override
    public void recordFailure() {
        stateReference.get().recordFailure();
    }

    /**
     * Records a successful call.
     */
    @Override
    public void recordSuccess() {
        stateReference.get().recordSuccess();
    }

    @Override
    public void incCurrentExecutions() {
        currentExecutions.incrementAndGet();
    }

    @Override
    public void decCurrentExecutions() {
        currentExecutions.decrementAndGet();
    }

    @Override
    public long getCurrentExecutions() {
        return currentExecutions.get();
    }

    /**
     * 获取当前熔断器状态 Get the state of this CircuitBreaker.
     *
     * @return the the state of this CircuitBreaker
     */
    @Override
    public State getState() {
        return this.stateReference.get().getState();
    }

    /**
     * Get the name of this CircuitBreaker.
     *
     * @return the the name of this CircuitBreaker
     */
    @Override
    public String getName() {
        return this.name;
    }

    /**
     * Get the config of this CircuitBreaker.
     *
     * @return the config of this CircuitBreaker
     */
    @Override
    public CircuitBreakerConfig getCircuitBreakerConfig() {
        return circuitBreakerConfig;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return "CircuitBreaker-" + Integer.toHexString(hashCode()) + "-" + this.name;
    }

    /**
     * 转换熔断器的状态
     * 
     * @param oldState
     *            老的状态
     * @param newState
     *            新的状态
     */
    protected void transition(State oldState, State newState) {
        synchronized (this) {
            State current = getState();
            if ((!current.equals(oldState)) || (current.equals(newState))) {
                return;
            }
            switch (newState) {
                case CLOSED:
                    stateReference.set(new ClosedState(this));
                    logger.warn(this + " has transition from " + current + " to CLOSE state.");
                    break;
                case OPEN:
                    stateReference.set(new OpenState(this));
                    logger.warn(this + " has transition from " + current + " to OPEN state.");
                    break;
                case HALF_OPEN:
                    stateReference.set(new HalfOpenState(this));
                    logger.warn(this + " has transition from " + current + " to HALF_OPEN state.");
                    break;
            }
        }
    }
}
